/**
  * Author: Mei
  * Date: 2020-12-30 19:13
  * Description: 合并tuple 把一个tuple中的每个元素为key，另一个为value
  *
  */
#include "../head.h"
#include <cassert>
#include<limits>

//输出tuple的元素
template<typename Arg>
void
GetArgByIndex1(int index, tuple<Arg> &tp) { //当tuple中只有一种类型的时候才会执行
    cout << get<0>(tp).first << ", " << get<0>(tp).second << endl;
};

template<typename Arg, typename ... Args>
void
GetArgByIndex1(int index, tuple<Arg, Args...> &tp) {
    if (index < 0 || index >= tuple_size<tuple<Arg, Args...>>::value) {
        throw invalid_argument("arg index out of range");
    }
    if (index > 0) {
        GetArgByIndex1(index - 1, (tuple<Args...> &) tp);
    } else {
        cout << get<0>(tp).first << ", " << get<0>(tp).second << endl;
    }
};
namespace details {
    //获取tuple的索引
    template<int...>
    struct TupleIndex {
    };

    template<int N, int ... indexes>
    struct MakeIndexes : MakeIndexes<N - 1, N - 1, indexes...> {

    };

    template<int ... indexes>
    struct MakeIndexes<0, indexes...> {
        typedef TupleIndex<indexes...> type;
    };

    template<size_t N, typename T1, typename T2>
    using pair_type = pair<typename tuple_element<N, T1>::type, typename tuple_element<N, T2>::type>;

    template<size_t N, typename T1, typename T2>
    pair_type<N, T1, T2> pair(const T1 &tup1, const T2 &tup2) {
        cout << "exec: N = " << N << endl;
        return make_pair(get<N>(tup1), get<N>(tup2));
    };

    template<int ...Indexes, typename T1, typename T2>
    auto
    pairs_helper(TupleIndex<Indexes...>, const T1 &tup1, const T2 &tup2)
    -> decltype(make_tuple(pair<Indexes>(tup1, tup2)...)) {
        auto mt = make_tuple(pair<Indexes>(tup1, tup2)...);
        for (int i = 0; i < tuple_size<decltype(mt)>::value; ++i) {
            GetArgByIndex1(i, mt);
        }
        return mt;
    };
}

template<typename Tuple1, typename Tuple2>
auto
//void
Zip(Tuple1 &&tup1, Tuple2 &&tup2)
-> decltype(details::pairs_helper(details::MakeIndexes<tuple_size<Tuple1>::value>::type(), tup1, tup2)) {
    static_assert(tuple_size<Tuple1>::value == tuple_size<Tuple2>::value, "tuples should be same size.");
    return details::pairs_helper(details::MakeIndexes<tuple_size<Tuple1>::value>::type(), tup1, tup2);
};

int main() {
    auto tp1 = make_tuple<int, short, double, char>(1, 2, 2.5, 'a');
    auto tp2 = make_tuple<double, short, double, char>(1.5, 2, 2.5, 'z');
    auto tp3 = Zip(tp1, tp2);
    //auto tp3 = details::pairs_helper(details::MakeIndexes<tuple_size<decltype(tp1)>::value>::type(), tp1, tp2);

    for (int i = 0; i < tuple_size<decltype(tp3)>::value; ++i) {
        GetArgByIndex1(i, tp3);
    }

    return 0;
}